/*
 * Copyright 2009 Sid Stamm <sid@sidstamm.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 * Sid Stamm <sid@sidstamm.com>
 */


const DEBUG = true; //true for debugging messages

//global helpers for debugging
function my_dump(x) {
  if(DEBUG) { dump("[CERTLOCK] " + x + "\n"); }
}
function my_dump_props(x, showValues) {
  if(!DEBUG) { return; }
  for(let p in x) {
    var thing = p;
    if(showValues) { thing = thing + " = " + x[p]; }
    my_dump("[" + thing + "]");
  }
}


var CertLockUtils = {

  /**
   * Debugging routine to display a small amount of cert information.
   * @param cert: nsIX509Cert instance.
   * @param dumpFunc: a callback that will print relevant data.
   */
dumpCert:
  function(cert, dumpFunc) {
    dumpFunc("CERTIFICATE for    " + cert.commonName);
    dumpFunc("   -> issuer:      " + cert.issuerCommonName);
    dumpFunc("   -> sha1 fprint: " + cert.sha1Fingerprint);
  },

debug:
  function util_debug(text) {
    my_dump(text);
  },

debug_props:
  function util_debug_props(text) {
    my_dump_props(text);
  },

  /**
   * Prints out an ASN1 tree structure
   * ... for debugging.
   *
   * @param asn: nsIASN1Object
   * @param level: tree depth (default 0)
   * @param printFcn: callback to print stuff.
   */
recursivePrintASN1:
  function util_recursivePrintASN1(asn, level, printFcn) {
    let indent = "";

    //build indent string
    for(let i=0; i<level; i++) { indent = indent + "\t \\"; }

    //spit out object
    printFcn(indent + " type (" + node.type + ")" 
              + node.displayName + "=" + node.displayValue);

    //recur through any sub-objects
    try {
      let seq = asn.QueryInterface(Ci.nsIASN1Sequence);

      if(seq && seq.isValidContainer && seq.ASN1Objects.length > 0) {
        for(var i = 0; i<seq.ASN1Objects.length; i++) {
          let node = seq.ASN1Objects.queryElementAt(i, Ci.nsIASN1Object);
          this.recursiveDumpASN1(node, level+1, printFcn);
        }
      }
    } catch (e) { /* punt */ }
  },


  /**
  * Walks through an ASN1 "tree" structure and finds a displayName that
  * matches the needle.
  *
  * @param asn: the nsIASN1Object structure to search
  * @param needle: string to match in an ASN1 node's name
  */
walkASN1ForMatch:
  function util_walkASN1ForMatch(asn, needle) {
    if(!asn) {
      return null; 
    }

    // matched what we are looking for!
    if(asn.displayName.match(needle)) {
      return asn; 
    }

    // look in children
    let seq = null;
    try {
      seq = asn.QueryInterface(Ci.nsIASN1Sequence);
    } catch (e) {} 

    if(!seq) { return null; }
    if(!seq.isValidContainer) { return null; }
    if(seq.ASN1Objects.length < 1) { return null; }

    for(var i = 0; i<seq.ASN1Objects.length; i++) {
      let node = seq.ASN1Objects.queryElementAt(i, Ci.nsIASN1Object);
      let res = util_walkASN1ForMatch(node, needle);
      if(res && res != null) { return res; }
    }
    return null;
  },

  /**
   * BROKEN! fixme
   * Should obtain the subject's public key from a cert (probably for use in
   * verifying a signature).
   *
   * @param cert: nsIX509Cert
   * @returns a string containing the base64-encoded DER-encoded key.
   */
getSubjectsPublicKey:
  function util_getSubjectsPublicKey(cert) {
    var asn1_item = this.walkASN1ForMatch(cert.ASN1Structure,
                                          "Subject's Public Key");
    var pkstr = asn1_item.QueryInterface(Ci.nsIASN1PrintableItem).value;
    my_dump_props(pkstr);
    //DER-encoded then base64 format
    dump("\n\n\n" + pkstr + "\n\n\n");
  },
};
